package com.ruoyi.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.fastjson2.JSON;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.ruoyi.common.core.enums.BMPayObjectTypeEnum;
import com.ruoyi.common.core.enums.PaymentStatusEnum;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.CommonStreamUtil;
import com.ruoyi.system.domain.BaseContest;
import com.ruoyi.system.domain.UpdatePaymentStatusAndPaymentDataReqDTO;
import com.ruoyi.system.domain.bm.BMCoach;
import com.ruoyi.system.domain.bm.BMPayment;
import com.ruoyi.system.domain.bm.BMPlayer;
import com.ruoyi.system.domain.bm.BMTeam;
import com.ruoyi.system.mapper.bm.BMCoachMapper;
import com.ruoyi.system.mapper.bm.BMPaymentMapper;
import com.ruoyi.system.mapper.bm.BMPlayerMapper;
import com.ruoyi.system.service.BaseWxPayCallbackService;
import com.ruoyi.system.service.BaseWxPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * BaseWxMpServiceImpl
 *
 * @author : cfx
 * @date : 2023/1/31 17:38
 **/
@Slf4j
@AllArgsConstructor
@Service
public class BaseWxPayCallbackServiceImpl implements BaseWxPayCallbackService {

    @Resource
    private BaseWxPayService baseWxPayService;

    @Resource
    private BMPaymentMapper paymentMapper;

    @Resource
    private BMPlayerMapper bmPlayerMapper;
    @Resource
    private BMCoachMapper bmCoachMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlePayNotify(WxPayNotifyV3Result xmlData) {
        WxPayNotifyV3Result.DecryptNotifyResult decryptRes = xmlData.getResult();
        try {
            log.info("微信支付回调数据解析, notifyResult: {}", xmlData);
            if (!WxPayConstants.WxpayTradeStatus.SUCCESS.equals(decryptRes.getTradeState())) {
                log.error("微信支付失败, error: {}", decryptRes.getTradeStateDesc());
                return;
            }
        } catch (Exception e) {
            log.error("微信支付回调数据解析异常, error: {}", e.getMessage(), e);
            throw new RuntimeException("微信支付回调数据解析异常");
        }

        String outOrderSn = decryptRes.getOutTradeNo();
        BMPayment payment = paymentMapper.isExistByOutOrderSn(outOrderSn);

        // 如果是已支付的记录, 则直接返回
        if (payment.getPaymentStatus() == PaymentStatusEnum.PAID.getId()) {
            return;
        }
        LocalDateTime paidTime = parseISO8601ToLocalDateTime(decryptRes.getSuccessTime());

        UpdatePaymentStatusAndPaymentDataReqDTO updatePaymentStatusAndPaymentDataReqDTO = new UpdatePaymentStatusAndPaymentDataReqDTO();
        updatePaymentStatusAndPaymentDataReqDTO.setId(payment.getId());
        updatePaymentStatusAndPaymentDataReqDTO.setPaidTime(paidTime);
        updatePaymentStatusAndPaymentDataReqDTO.setTradeId(decryptRes.getTransactionId());
        if (payment.getPaymentStatus() == PaymentStatusEnum.WAITING_PAY.getId()) {
            paymentMapper.updatePaymentStatusAndPaymentData(updatePaymentStatusAndPaymentDataReqDTO, PaymentStatusEnum.PAID.getId());
        }
        if (Objects.equals(payment.getObjectType(), BMPayObjectTypeEnum.TEAM.getCode())){
            //球队支付
            List<BMPlayer> playerList = bmPlayerMapper.playerListByTeamId(payment.getObjectId());
            if (CollUtil.isEmpty(playerList)){
                throw new ServiceException("请添加球员!");
            }
            Set<Long> playerIds = CommonStreamUtil.transSet(playerList, BMPlayer::getId);
            List<BMPayment> bmPayments = paymentMapper.selectByObjectIds(BMPayObjectTypeEnum.PLAYER.getCode(), playerIds);

            //获取未支付
            List<BMPayment> paidNotPayList = CommonStreamUtil.filter(bmPayments, bo -> bo.getPaymentStatus() == PaymentStatusEnum.WAITING_PAY.getId());
            if (CollUtil.isEmpty(paidNotPayList)){
                return;
            }
            paidNotPayList.forEach(bo -> {
                UpdatePaymentStatusAndPaymentDataReqDTO updateDTO = new UpdatePaymentStatusAndPaymentDataReqDTO();
                updateDTO.setId(bo.getId());
                updateDTO.setPaidTime(paidTime);
                updateDTO.setTradeId(decryptRes.getTransactionId());
                paymentMapper.updatePaymentStatusAndPaymentData(updateDTO, PaymentStatusEnum.PAID.getId());
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void mockHandlePayNotify(WxPayOrderNotifyResult notifyResult) {
//        innerHandlePayNotify(notifyResult);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleRefundNotify(WxPayRefundNotifyV3Result xmlData) {
        WxPayRefundNotifyV3Result.DecryptNotifyResult decryptRes = xmlData.getResult();
        try {
            log.info("微信退款回调数据解析, notifyResult: {}", xmlData);
            if (!WxPayConstants.RefundStatus.SUCCESS.equals(decryptRes.getRefundStatus())) {
                log.error("微信退款失败");
                return;
            }
        } catch (Exception e) {
            log.error("微信退款回调数据解析异常, error: {}", e.getMessage(), e);
            throw new RuntimeException("微信退款回调数据解析异常");
        }
        String outOrderSn = decryptRes.getOutTradeNo();
        BMPayment payment = paymentMapper.isExistByOutOrderSn(outOrderSn);
        // 如果是已退款的记录, 则直接返回
        if (payment.getPaymentStatus() == PaymentStatusEnum.REFUND_PAID.getId()) {
            return;
        }
        LocalDateTime paidTime = parseISO8601ToLocalDateTime(decryptRes.getSuccessTime());

        if (payment.getPaymentStatus() == PaymentStatusEnum.APPLY_REFUNDING.getId()) {
            paymentMapper.updatePaymentStatusAndRefund(payment.getId(),paidTime,decryptRes.getTransactionId(), PaymentStatusEnum.REFUND_PAID.getId());
        }
        //调整球员 软删除
        if (Objects.equals(payment.getObjectType(), BMPayObjectTypeEnum.PLAYER.getCode())){
            BMPlayer player = bmPlayerMapper.selectById(payment.getObjectId());
            if (Objects.nonNull( player)){
                player.setIsDeleted(1L);
                bmPlayerMapper.updateById(player);
            }
        } else if (Objects.equals(payment.getObjectType(), BMPayObjectTypeEnum.COACH.getCode())) {
            BMCoach bmCoach = bmCoachMapper.selectById(payment.getObjectId());
            if (Objects.nonNull(bmCoach)){
                bmCoach.setIsDeleted(1L);
                bmCoachMapper.updateById(bmCoach);
            }
        }
    }

    /**
     * 解析时间
     */
    /**
     * 将ISO 8601格式的日期字符串解析为LocalDateTime对象
     * @param isoDateString ISO 8601格式的日期字符串，如 "2025-06-30T23:37:19+08:00"
     * @return 解析后的LocalDateTime对象
     * @throws DateTimeParseException 如果解析失败
     */
    public static LocalDateTime parseISO8601ToLocalDateTime(String isoDateString) {
        // 方法1：使用DateTimeFormatter.ISO_OFFSET_DATE_TIME解析
        // 这种方法适用于带有时区偏移量的ISO 8601格式
        DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
        OffsetDateTime offsetDateTime = OffsetDateTime.parse(isoDateString, formatter);
        return offsetDateTime.toLocalDateTime();

        // 方法2：使用ZonedDateTime解析（如果需要处理完整的时区信息）
        // ZonedDateTime zonedDateTime = ZonedDateTime.parse(isoDateString);
        // return zonedDateTime.toLocalDateTime();
    }
}
